package bytecode;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理实现机制
 */
public class DynamicSubject implements InvocationHandler {

    private Object sub;

    public DynamicSubject(Object obj) {
        this.sub = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before calling: " + method);

        method.invoke(this.sub, args);

        System.out.println("after calling: " + method);

        return null;
    }

    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        Subject realSubject = new RealSubject();

        InvocationHandler dsih = new DynamicSubject(realSubject);
        Class<?> aClass = realSubject.getClass();
        Subject subject = (Subject) Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), dsih);
        // 1.对 Proxy源码分析过程
        // 定位到内部类 java.lang.reflect.Proxy.ProxyClassFactory  aplly方法  639行
        // byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        //                proxyName, interfaces, accessFlags);
        // 生成代理类的字节数组：proxyClassFile ，从而经过classload 生成代理类实例

        //2. proxyClassFile的生成  sun.misc.ProxyGenerator.generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
        // generateProxyClass 方法内，可以通过设置saveGeneratedFiles = true，来将生成的class字节($Proxy0.class)保存到磁盘上
        // System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        //3.对 $Proxy0.class 分析，结合Proxy的javaDoc
        //以当前例子分析 DynamicSubject
        // 3.1 对代理实例$Proxy0调用 hashCode ， equals ，或toString 将被编码并分派到调用处理程序(DynamicSubject)的invoke中执行，
        // 就是说hashCode ， equals ，或toString也被代理了，和 接口方法(这里就是 Subject中request)调用相同的方式进行编码和调度，
        // 如上所述。该声明类的Method对象(例如实现代理realSubject.request())传递给invoke将java.lang.Object 。
        // 3.2 从继承的代理实例的其他公共方法java.lang.Object没有被代理类重写，(其他公共方法：getClass(),clone(),notify()。。等等)
        // 这些方法的调用行为与对java.lang.Object实例的调用类似。
/*
Proxy的javaDoc 摘取
An invocation of the hashCode,equals, or toString
methods declared in java.lang.Object on a proxy instance will
be encoded and dispatched to the invocation handler's invoke method
in the same manner as interface method invocations are encoded and dispatched,
as described above. The declaring class of the Method object passed to invoke will be java.lang.Object.
Other public methods of a proxy instance inherited from java.lang.Object are not overridden by a proxy class,
so invocations of those methods behave like they do for instances of java.lang.Object.
对代理实例上java.lang.Object中声明的hashCode、equals或toString方法的调用将被编码并发送到调用处理程序的invoke方法中，
其方式与对接口方法调用进行编码和发送的方式相同，
如上所述。传递给调用的方法对象的声明类将是java.lang.object。
从java.lang.Object继承的代理实例的其他公共方法没有被代理类重写，因此这些方法的调用行为与对java.lang.Object实例的调用类似。
 */


        subject.request();

        //  subject 的真实类型？程序运行期动态生成的
        System.out.println(subject.getClass()); //class bytecode.$Proxy0
        System.out.println(subject.getClass().getSuperclass()); //class java.lang.reflect.Proxy
    }
}

interface Subject {
    void request();
}

class RealSubject implements Subject {

    @Override
    public void request() {
        System.out.println("RealSubject calling: request ");
    }
}
